home *** CD-ROM | disk | FTP | other *** search
Lex Description | 1993-03-16 | 19.0 KB | 684 lines | [TEXT/tefi] |
- %{
- # line 3 "prelang.y"
- /* The above line is to give proper line number references. Please mail me
- * if your compiler complains about it.
- */
- /*
- * This is the grammar definition of LPC. The token table is built
- * automatically by make_func. The lang.y is constructed from this file,
- * the generated token list and post_lang.y. The reason of this is that there
- * is no #include-statment that yacc recognizes.
- */
- #include <string.h>
- #include <stdio.h>
- #include <memory.h>
- #if defined(sun)
- #include <alloca.h>
- #endif
-
- #include "lint.h"
- #include "interpret.h"
- #include "object.h"
- #include "exec.h"
- #include "config.h"
- #include "instrs.h"
- #include "incralloc.h"
- #include "switch.h"
- #include "rc.h"
-
- #if defined(__GNUC__) && !defined(lint) && !defined(DEBUG)
- #define INLINE inline
- #else
- #define INLINE
- #endif
-
- #define YYMAXDEPTH 600
-
- /* NUMPAREAS areas are saved with the program code after compilation.
- */
- #define A_PROGRAM 0
- #define A_FUNCTIONS 1
- #define A_STRINGS 2
- #define A_VARIABLES 3
- #define A_LINENUMBERS 4
- #define A_INHERITS 5
- #define A_ARGUMENT_TYPES 6
- #define A_ARGUMENT_INDEX 7
- #define NUMPAREAS 8
- #define A_CASE_NUMBERS 8
- #define A_CASE_STRINGS 9
- #define A_CASE_LABELS 10
- #define NUMAREAS 11
-
- #define BREAK_ON_STACK 0x40000
- #define BREAK_FROM_CASE 0x80000
-
- /* make shure that this struct has a size that is a power of two */
- struct case_heap_entry { int key; short addr; short line; };
- #define CASE_HEAP_ENTRY_ALIGN(offset) offset &= -sizeof(struct case_heap_entry)
-
- static struct mem_block mem_block[NUMAREAS];
-
- /*
- * Some good macros to have.
- */
-
- #define BASIC_TYPE(e,t) ((e) == TYPE_ANY ||\
- (e) == (t) ||\
- (t) == TYPE_ANY)
-
- #define TYPE(e,t) (BASIC_TYPE((e) & TYPE_MOD_MASK, (t) & TYPE_MOD_MASK) ||\
- (((e) & TYPE_MOD_POINTER) && ((t) & TYPE_MOD_POINTER) &&\
- BASIC_TYPE((e) & (TYPE_MOD_MASK & ~TYPE_MOD_POINTER),\
- (t) & (TYPE_MOD_MASK & ~TYPE_MOD_POINTER))))
-
- #define FUNCTION(n) ((struct function *)mem_block[A_FUNCTIONS].block + (n))
- #define VARIABLE(n) ((struct variable *)mem_block[A_VARIABLES].block + (n))
-
- #define align(x) (((x) + 3) & ~3)
-
- /*
- * If the type of the function is given, then strict types are
- * checked and required.
- */
- static int exact_types;
- extern int pragma_strict_types; /* Maintained by lex.c */
- extern int pragma_save_types; /* Also maintained by lex.c */
- int approved_object; /* How I hate all these global variables */
-
- extern int total_num_prog_blocks, total_prog_block_size;
-
- extern int num_parse_error;
- extern int d_flag;
- static int heart_beat; /* Number of the heart beat function */
-
- static int current_break_address;
- static int current_continue_address;
- static int current_case_number_heap;
- static int current_case_string_heap;
- #define SOME_NUMERIC_CASE_LABELS 0x40000
- #define NO_STRING_CASE_LABELS 0x80000
- static int zero_case_label;
- static int current_type;
-
- static int last_push_indexed;
- static int last_push_local;
- static int last_push_identifier;
-
- /*
- * There is always function starting at address 0, which will execute
- * the initialization code. This code is spread all over the program,
- * with jumps to next initializer. The next variable keeps track of
- * the previous jump. After the last initializer, the jump will be changed
- * into a return(0) statement instead.
- *
- * A function named '__INIT' will be defined, which will contain the
- * initialization code. If there was no initialization code, then the
- * function will not be defined. That is the usage of the
- * first_last_initializer_end variable.
- *
- * When inheriting from another object, a call will automatically be made
- * to call __INIT in that code from the current __INIT.
- */
- static int last_initializer_end;
- static int first_last_initializer_end;
-
- static struct program NULL_program; /* marion - clean neat empty struct */
-
- void epilog();
- static int check_declared PROT((char *str));
- static void prolog();
- static char *get_two_types PROT((int type1, int type2));
- void free_all_local_names(),
- add_local_name PROT((char *, int)), smart_log PROT((char *, int, char *));
- extern int yylex();
- extern char *findstring PROT((char *));
- static int verify_declared PROT((char *));
- static void copy_variables();
- static int copy_functions PROT((struct program *, int type));
- void type_error PROT((char *, int));
-
- char *xalloc(), *string_copy();
-
- extern int current_line;
- /*
- * 'inherit_file' is used as a flag. If it is set to a string
- * after yyparse(), this string should be loaded as an object,
- * and the original object must be loaded again.
- */
- extern char *current_file, *inherit_file;
-
- /*
- * The names and types of arguments and auto variables.
- */
- char **local_names;
- unsigned short *type_of_locals;
- int current_number_of_locals = 0;
- int current_break_stack_need = 0 ,max_break_stack_need = 0;
-
- /*
- * The types of arguments when calling functions must be saved,
- * to be used afterwards for checking. And because function calls
- * can be done as an argument to a function calls,
- * a stack of argument types is needed. This stack does not need to
- * be freed between compilations, but will be reused.
- */
- static struct mem_block type_of_arguments;
-
- struct program *prog; /* Is returned to the caller of yyparse */
-
- void init_compiler(void)
- {
- memset(&mem_block, 0, sizeof(struct mem_block) * NUMAREAS);
- memset(&type_of_arguments, 0, sizeof(type_of_arguments));
- }
-
- /*
- * Compare two types, and return true if they are compatible.
- */
- static int compatible_types(t1, t2)
- int t1, t2;
- {
- if (t1 == TYPE_UNKNOWN || t2 == TYPE_UNKNOWN)
- return 0;
- if (t1 == t2)
- return 1;
- if (t1 == TYPE_ANY || t2 == TYPE_ANY)
- return 1;
- if ((t1 & TYPE_MOD_POINTER) && (t2 & TYPE_MOD_POINTER)) {
- if ((t1 & TYPE_MOD_MASK) == (TYPE_ANY|TYPE_MOD_POINTER) ||
- (t2 & TYPE_MOD_MASK) == (TYPE_ANY|TYPE_MOD_POINTER))
- return 1;
- }
- return 0;
- }
-
- /*
- * Add another argument type to the argument type stack
- */
- INLINE
- static void add_arg_type(type)
- unsigned short type;
- {
- struct mem_block *mbp = &type_of_arguments;
- while (mbp->current_size + sizeof type > mbp->max_size) {
- mbp->max_size <<= 1;
- mbp->block = xrealloc((char *)mbp->block, mbp->max_size);
- }
- memcpy(mbp->block + mbp->current_size, &type, sizeof type);
- mbp->current_size += sizeof type;
- }
-
- /*
- * Pop the argument type stack 'n' elements.
- */
- INLINE
- static void pop_arg_stack(n)
- int n;
- {
- type_of_arguments.current_size -= sizeof (unsigned short) * n;
- }
-
- /*
- * Get type of argument number 'arg', where there are
- * 'n' arguments in total in this function call. Argument
- * 0 is the first argument.
- */
- INLINE
- int get_argument_type(arg, n)
- int arg, n;
- {
- return
- ((unsigned short *)
- (type_of_arguments.block + type_of_arguments.current_size))[arg - n];
- }
-
- INLINE
- static void add_to_mem_block(n, data, size)
- int n, size;
- char *data;
- {
- struct mem_block *mbp = &mem_block[n];
- while (mbp->current_size + size > mbp->max_size) {
- mbp->max_size <<= 1;
- mbp->block = xrealloc((char *)mbp->block, mbp->max_size);
- }
- memcpy(mbp->block + mbp->current_size, data, size);
- mbp->current_size += size;
- }
-
- static void ins_byte(b)
- char b;
- {
- add_to_mem_block(A_PROGRAM, &b, 1);
- }
-
- /*
- * Store a 2 byte number. It is stored in such a way as to be sure
- * that correct byte order is used, regardless of machine architecture.
- * Also beware that some machines can't write a word to odd addresses.
- */
- static void ins_short(l)
- short l;
- {
- add_to_mem_block(A_PROGRAM, (char *)&l + 0, 1);
- add_to_mem_block(A_PROGRAM, (char *)&l + 1, 1);
- }
-
- static void upd_short(offset, l)
- int offset;
- short l;
- {
- mem_block[A_PROGRAM].block[offset + 0] = ((char *)&l)[0];
- mem_block[A_PROGRAM].block[offset + 1] = ((char *)&l)[1];
- }
-
- static short read_short(offset)
- int offset;
- {
- short l;
-
- ((char *)&l)[0] = mem_block[A_PROGRAM].block[offset + 0];
- ((char *)&l)[1] = mem_block[A_PROGRAM].block[offset + 1];
- return l;
- }
-
- /*
- * Store a 4 byte number. It is stored in such a way as to be sure
- * that correct byte order is used, regardless of machine architecture.
- */
- static void ins_long(l)
- int l;
- {
- add_to_mem_block(A_PROGRAM, (char *)&l+0, 1);
- add_to_mem_block(A_PROGRAM, (char *)&l+1, 1);
- add_to_mem_block(A_PROGRAM, (char *)&l+2, 1);
- add_to_mem_block(A_PROGRAM, (char *)&l+3, 1);
- }
-
- static void ins_f_byte(b)
- unsigned int b;
- {
- ins_byte((char)(b - F_OFFSET));
- }
-
- /*
- * Return the index of the function found, otherwise -1.
- */
- static int defined_function(s, start)
- char *s;
- int start;
- {
- int offset;
- struct function *funp;
- char *interned;
-
- if (interned = findstring(s)) /* Only search if amongst strings */
- for (offset = start * sizeof (struct function);
- offset < mem_block[A_FUNCTIONS].current_size;
- offset += sizeof (struct function))
- {
- funp = (struct function *)&mem_block[A_FUNCTIONS].block[offset];
- if (funp->flags & NAME_HIDDEN)
- continue;
- if (funp->name == interned)
- return offset / sizeof (struct function);
- }
- return -1;
- }
-
- /*
- * A mechanism to remember addresses on a stack. The size of the stack is
- * defined in config.h.
- */
- static int comp_stackp;
- int *comp_stack;
-
- static void push_address() {
- if (comp_stackp >= COMPILER_STACK_SIZE) {
- yyerror("Compiler stack overflow");
- comp_stackp++;
- return;
- }
- comp_stack[comp_stackp++] = mem_block[A_PROGRAM].current_size;
- }
-
- static void push_explicit(address)
- int address;
- {
- if (comp_stackp >= COMPILER_STACK_SIZE) {
- yyerror("Compiler stack overflow");
- comp_stackp++;
- return;
- }
- comp_stack[comp_stackp++] = address;
- }
-
- static int pop_address() {
- if (comp_stackp == 0)
- fatal("Compiler stack underflow.\n");
- if (comp_stackp > COMPILER_STACK_SIZE) {
- --comp_stackp;
- return 0;
- }
- return comp_stack[--comp_stackp];
- }
-
- /*
- * Patch a function definition of an inherited function, to what it really
- * should be.
- * The name of the function can be one of:
- * object::name
- * ::name
- * name
- * Where 'object' is the name of the superclass.
- */
- static void find_inherited(funp)
- struct function *funp;
- {
- int i;
- struct inherit *ip;
- int num_inherits, super_length;
- char *real_name, *super_name = 0, *p;
- char *interned;
-
- real_name = funp->name;
- if (real_name[0] == ':')
- real_name = real_name + 2; /* There will be exactly two ':' */
- else if (p = strchr(real_name, ':')) {
- real_name = p+2;
- super_name = funp->name;
- super_length = real_name - super_name - 2;
- }
- num_inherits = mem_block[A_INHERITS].current_size /
- sizeof (struct inherit);
- ip = &((struct inherit *)mem_block[A_INHERITS].block)[num_inherits-1];
- if (interned = findstring(real_name)) /* Only search if amongst strings */
- for (; num_inherits > 0; ip--, num_inherits--) {
- if (super_name) {
- int l = strlen(ip->prog->name); /* Including .c */
- if (l - 2 < super_length)
- continue;
- if (strncmp(super_name, ip->prog->name + l - 2 - super_length,
- super_length) != 0)
- continue;
- }
- for (i=0; i < ip->prog->num_functions; i++) {
- if (ip->prog->functions[i].flags & (NAME_UNDEFINED|NAME_HIDDEN))
- continue;
- if (ip->prog->functions[i].type & TYPE_MOD_PRIVATE)
- continue;
- if (ip->prog->functions[i].name != interned)
- continue;
- funp->offset = ip - (struct inherit *)mem_block[A_INHERITS].block;
- funp->flags = ip->prog->functions[i].flags | NAME_INHERITED;
- funp->num_local = ip->prog->functions[i].num_local;
- funp->num_arg = ip->prog->functions[i].num_arg;
- funp->type = ip->prog->functions[i].type;
- funp->type |= funp->type & TYPE_MOD_PUBLIC ?
- ip->type & ~TYPE_MOD_PRIVATE : ip->type;
- funp->function_index_offset = i;
- return;
- }
- }
- return;
- }
-
- void replace_function(num, name, num_arg, num_local, offset, flags, type)
- int num, num_arg, num_local, offset, flags, type;
- char *name;
- {
- struct function *funp;
-
- funp = (struct function *)(mem_block[A_FUNCTIONS].block) + num;
- if (!(funp->flags & NAME_UNDEFINED) &&
- !(flags & NAME_PROTOTYPE) &&
- !(funp->flags & NAME_INHERITED))
- {
- char buff[500];
- sprintf(buff, "Redeclaration of function %s.", name);
- yyerror(buff);
- return;
- }
- /*
- * It was either an undefined but used function, or an inherited
- * function. In both cases, we now consider this to be THE new
- * definition. It might also have been a prototype to an already
- * defined function.
- *
- * Check arguments only when types are supposed to be tested,
- * and if this function really has been defined already.
- *
- * 'nomask' functions may not be redefined.
- */
- if ((funp->type & TYPE_MOD_NO_MASK) &&
- !(funp->flags & NAME_PROTOTYPE) &&
- !(flags & NAME_PROTOTYPE))
- {
- char *p = (char *)alloca(80 + strlen(name));
- sprintf(p, "Illegal to redefine 'nomask' function \"%s\"",name);
- yyerror(p);
- }
- if (exact_types && funp->type != TYPE_UNKNOWN) {
- int i;
- if (funp->num_arg != num_arg && !(funp->type & TYPE_MOD_VARARGS))
- yyerror("Incorrect number of arguments.");
- else if (!(funp->flags & NAME_STRICT_TYPES))
- yyerror("Called function not compiled with type testing.");
- else {
- /* Now check that argument types wasn't changed. */
- for (i=0; i < num_arg; i++) {
- }
- }
- }
- /* If it was yet another prototype, then simply return. */
- if (flags & NAME_PROTOTYPE)
- return;
- funp->num_arg = num_arg;
- funp->num_local = num_local;
- funp->flags = flags;
- funp->offset = offset;
- funp->function_index_offset = 0;
- funp->type = type;
- if (exact_types)
- funp->flags |= NAME_STRICT_TYPES;
- return;
- }
-
- /*
- * Define a new function. Note that this function is called at least twice
- * for alll function definitions. First as a prototype, then as the real
- * function. Thus, there are tests to avoid generating error messages more
- * than once by looking at (flags & NAME_PROTOTYPE).
- */
- static int define_new_function(name, num_arg, num_local, offset, flags, type)
- char *name;
- int num_arg, num_local;
- int offset, flags, type;
- {
- int num;
- struct function fun;
- unsigned short argument_start_index;
-
- num = defined_function(name, 0);
- if (num >= 0) {
- int save_num;
-
- /*
- * The function was already defined. It may be one of several reasons:
- *
- * 1. There has been a prototype.
- * 2. There was the same function defined by inheritance.
- * 3. This function has been called, but not yet defined.
- * 4. The function is doubly defined.
- * 5. A "late" prototype has been encountered.
- */
- do {
- replace_function(num, name, num_arg, num_local, offset, flags,type);
- save_num = num;
- num = defined_function(name, num+1);
- } while (num >= 0);
- return save_num;
- }
- if (strcmp(name, "heart_beat") == 0)
- heart_beat = mem_block[A_FUNCTIONS].current_size /
- sizeof (struct function);
- fun.name = make_shared_string(name);
- fun.offset = offset;
- fun.flags = flags;
- fun.num_arg = num_arg;
- fun.num_local = num_local;
- fun.function_index_offset = 0;
- fun.type = type;
- if (exact_types)
- fun.flags |= NAME_STRICT_TYPES;
- num = mem_block[A_FUNCTIONS].current_size / sizeof fun;
- /* Number of local variables will be updated later */
- add_to_mem_block(A_FUNCTIONS, (char *)&fun, sizeof fun);
-
- if (exact_types == 0 || num_arg == 0) {
- argument_start_index = INDEX_START_NONE;
- } else {
- int i;
-
- /*
- * Save the start of argument types.
- */
- argument_start_index =
- mem_block[A_ARGUMENT_TYPES].current_size /
- sizeof (unsigned short);
- for (i=0; i < num_arg; i++) {
- add_to_mem_block(A_ARGUMENT_TYPES, &type_of_locals[i],
- sizeof type_of_locals[i]);
- }
- }
- add_to_mem_block(A_ARGUMENT_INDEX, &argument_start_index,
- sizeof argument_start_index);
- return num;
- }
-
- static void define_variable(name, type, flags)
- char *name;
- int type;
- int flags;
- {
- struct variable dummy;
- int n;
-
- n = check_declared(name);
- if (n != -1 && (VARIABLE(n)->type & TYPE_MOD_NO_MASK)) {
- char *p = (char *)alloca(80 + strlen(name));
- sprintf(p, "Illegal to redefine 'nomask' variable \"%s\"", name);
- yyerror(p);
- }
- dummy.name = make_shared_string(name);
- dummy.type = type;
- dummy.flags = flags;
- add_to_mem_block(A_VARIABLES, (char *)&dummy, sizeof dummy);
- }
-
- short store_prog_string(str)
- char *str;
- {
- short i;
- char **p;
-
- p = (char **) mem_block[A_STRINGS].block;
- str = make_shared_string(str);
- for (i=mem_block[A_STRINGS].current_size / sizeof str -1; i>=0; --i)
- if (p[i] == str) {
- free_string(str); /* Needed as string is only free'ed once. */
- return i;
- }
-
- add_to_mem_block(A_STRINGS, &str, sizeof str);
- return mem_block[A_STRINGS].current_size / sizeof str - 1;
- }
-
- void add_to_case_heap(block_index,entry)
- int block_index;
- struct case_heap_entry *entry;
- {
- char *heap_start;
- int offset,parent;
- int current_heap;
-
- if ( block_index == A_CASE_NUMBERS )
- current_heap = current_case_number_heap;
- else
- current_heap = current_case_string_heap;
- offset = mem_block[block_index].current_size - current_heap;
- add_to_mem_block(block_index, (char*)entry, sizeof(*entry) );
- heap_start = mem_block[block_index].block + current_heap;
- for ( ; offset; offset = parent ) {
- parent = ( offset - sizeof(struct case_heap_entry) ) >> 1 ;
- CASE_HEAP_ENTRY_ALIGN(parent);
- if ( ((struct case_heap_entry*)(heap_start+offset))->key <
- ((struct case_heap_entry*)(heap_start+parent))->key )
- {
- *(struct case_heap_entry*)(heap_start+offset) =
- *(struct case_heap_entry*)(heap_start+parent);
- *(struct case_heap_entry*)(heap_start+parent) = *entry;
- }
- }
- }
-
- /*
- * Arrange a jump to the current position for the initialization code
- * to continue.
- */
- static void transfer_init_control() {
- if (mem_block[A_PROGRAM].current_size - 2 == last_initializer_end)
- mem_block[A_PROGRAM].current_size -= 3;
- else {
- /*
- * Change the address of the last jump after the last
- * initializer to this point.
- */
- upd_short(last_initializer_end,
- mem_block[A_PROGRAM].current_size);
- }
- }
-
- void add_new_init_jump();
- %}
-
- /*
- * These values are used by the stack machine, and can not be directly
- * called from LPC.
- */
- %token F_JUMP F_JUMP_WHEN_ZERO F_JUMP_WHEN_NON_ZERO
- %token F_POP_VALUE F_DUP
- %token F_STORE F_CALL_FUNCTION_BY_ADDRESS
- %token F_PUSH_IDENTIFIER_LVALUE F_PUSH_LOCAL_VARIABLE_LVALUE
- %token F_PUSH_INDEXED_LVALUE F_INDIRECT F_INDEX
- %token F_CONST0 F_CONST1
-
- /*
- * These are the predefined functions that can be accessed from LPC.
- */
-
- %token F_IF F_IDENTIFIER F_LAND F_LOR F_STATUS
- %token F_RETURN F_STRING
- %token F_INC F_DEC
- %token F_POST_INC F_POST_DEC F_COMMA
- %token F_NUMBER F_ASSIGN F_INT F_ADD F_SUBTRACT F_MULTIPLY
- %token F_DIVIDE F_LT F_GT F_EQ F_GE F_LE
- %token F_NE
- %token F_ADD_EQ F_SUB_EQ F_DIV_EQ F_MULT_EQ
- %token F_NEGATE
- %token F_SUBSCRIPT F_WHILE F_BREAK
- %token F_DO F_FOR F_SWITCH
- %token F_SSCANF F_PARSE_COMMAND F_STRING_DECL F_LOCAL_NAME
- %token F_ELSE F_DESCRIBE
- %token F_CONTINUE
- %token F_MOD F_MOD_EQ F_INHERIT F_COLON_COLON
- %token F_STATIC
- %token F_ARROW F_AGGREGATE F_M_AGGREGATE
- %token F_COMPL F_AND F_AND_EQ F_OR F_OR_EQ F_XOR F_XOR_EQ
- %token F_LSH F_LSH_EQ F_RSH F_RSH_EQ
- %token F_CATCH
- %token F_OBJECT F_VOID F_MIXED F_PRIVATE F_NO_MASK F_NOT F_MAPPING
- %token F_PROTECTED F_PUBLIC
- %token F_VARARGS
-